﻿Imports System
Imports System.Reflection
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.ComponentModel.Compiler
    Friend NotInheritable Class PropertyBindValidator
        Inherits Validator
        ' Methods
        Public Overrides Function Validate(ByVal manager As ValidationManager, ByVal obj As Object) As ValidationErrorCollection
            Dim errors As ValidationErrorCollection = MyBase.Validate(manager, obj)
            Dim bind As PropertyBind = [TryCast](obj,PropertyBind)
            If (bind Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_UnexpectedArgumentType", New Object() { GetType(PropertyBind).FullName }), "obj")
            End If
            Dim validationContext As PropertyValidationContext = [TryCast](manager.Context.Item(GetType(PropertyValidationContext)),PropertyValidationContext)
            If (validationContext Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(BindValidationContext).Name }))
            End If
            Dim activity As Activity = [TryCast](manager.Context.Item(GetType(Activity)),Activity)
            If (activity Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(Activity).Name }))
            End If
            Dim item As ValidationError = Nothing
            If String.IsNullOrEmpty(bind.Name) Then
                item = New ValidationError(SR.GetString("Error_PropertyNotSet", New Object() { "Name" }), &H116) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name") _
                }
            Else
                Dim context2 As BindValidationContext = [TryCast](manager.Context.Item(GetType(BindValidationContext)),BindValidationContext)
                If (context2 Is Nothing) Then
                    Dim baseType As Type = BindHelpers.GetBaseType(manager, validationContext)
                    If (Not baseType Is Nothing) Then
                        Dim accessType As AccessTypes = BindHelpers.GetAccessType(manager, validationContext)
                        context2 = New BindValidationContext(baseType, accessType)
                    End If
                End If
                If (Not context2 Is Nothing) Then
                    Dim targetType As Type = context2.TargetType
                    If (item Is Nothing) Then
                        errors.AddRange(Me.ValidateBindProperty(manager, activity, bind, New BindValidationContext(targetType, context2.Access)))
                    End If
                End If
            End If
            If (Not item Is Nothing) Then
                errors.Add(item)
            End If
            Return errors
        End Function

        Private Function ValidateBindProperty(ByVal manager As ValidationManager, ByVal activity As Activity, ByVal bind As PropertyBind, ByVal validationContext As BindValidationContext) As ValidationErrorCollection
            Dim errors As New ValidationErrorCollection
            Dim checkTypes As Boolean = False
            Dim service As IWorkflowCompilerOptionsService = [TryCast](manager.GetService(GetType(IWorkflowCompilerOptionsService)),IWorkflowCompilerOptionsService)
            If (Not service Is Nothing) Then
                checkTypes = service.CheckTypes
            End If
            Dim name As String = bind.Name
            Dim enclosingActivity As Activity = Helpers.GetEnclosingActivity(activity)
            Dim refActivity As Activity = enclosingActivity
            If ((name.IndexOf("."c) <> -1) AndAlso (Not refActivity Is Nothing)) Then
                refActivity = Helpers.GetDataSourceActivity(activity, bind.Name, name)
            End If
            If (refActivity Is Nothing) Then
                Dim error As New ValidationError(SR.GetString("Error_NoEnclosingContext", New Object() { activity.Name }), &H130) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name") _
                }
                errors.Add([error])
                Return errors
            End If
            Dim item As ValidationError = Nothing
            Dim property As PropertyInfo = Nothing
            Dim activityType As Type = Nothing
            If ([property] Is Nothing) Then
                activityType = BindValidatorHelper.GetActivityType(manager, refActivity)
                If (Not activityType Is Nothing) Then
                    [property] = activityType.GetProperty(name, (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance)))
                End If
            End If
            If (activityType Is Nothing) Then
                item = New ValidationError(SR.GetString("Error_TypeNotResolvedInPropertyName", New Object() { "Name" }), &H163) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            ElseIf ([property] Is Nothing) Then
                item = New ValidationError(SR.GetString("Error_PropertyNotExists", New Object() { MyBase.GetFullPropertyName(manager), name }), &H164) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            ElseIf Not [property].CanRead Then
                item = New ValidationError(SR.GetString("Error_PropertyReferenceNoGetter", New Object() { MyBase.GetFullPropertyName(manager), name }), 360) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            ElseIf ([property].GetGetMethod Is Nothing) Then
                item = New ValidationError(SR.GetString("Error_PropertyReferenceGetterNoAccess", New Object() { MyBase.GetFullPropertyName(manager), name }), &H60A) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            ElseIf (((Not refActivity Is enclosingActivity) AndAlso Not [property].GetGetMethod.IsAssembly) AndAlso Not [property].GetGetMethod.IsPublic) Then
                item = New ValidationError(SR.GetString("Error_PropertyNotAccessible", New Object() { MyBase.GetFullPropertyName(manager), name }), &H165) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            ElseIf ([property].PropertyType Is Nothing) Then
                item = New ValidationError(SR.GetString("Error_PropertyTypeNotResolved", New Object() { MyBase.GetFullPropertyName(manager), name }), &H166) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            Else
                Dim memberInfo As MemberInfo = [property]
                If (((bind.Path Is Nothing) OrElse (bind.Path.Length = 0)) AndAlso ((Not validationContext.TargetType Is Nothing) AndAlso Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, [property].PropertyType, validationContext.Access))) Then
                    item = New ValidationError(SR.GetString("Error_PropertyTypeMismatch", New Object() { MyBase.GetFullPropertyName(manager), [property].PropertyType.FullName, validationContext.TargetType.FullName }), &H167) { _
                        .PropertyName = MyBase.GetFullPropertyName(manager) _
                    }
                ElseIf Not String.IsNullOrEmpty(bind.Path) Then
                    memberInfo = MemberBind.GetMemberInfo([property].PropertyType, bind.Path)
                    If (memberInfo Is Nothing) Then
                        item = New ValidationError(SR.GetString("Error_InvalidMemberPath", New Object() { name, bind.Path }), 300) { _
                            .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                        }
                    Else
                        If checkTypes Then
                            item = MemberBind.ValidateTypesInPath([property].PropertyType, bind.Path)
                            If (Not item Is Nothing) Then
                                item.PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path")
                            End If
                        End If
                        If (item Is Nothing) Then
                            Dim memberType As Type = If(TypeOf memberInfo Is FieldInfo, [TryCast](memberInfo,FieldInfo).FieldType, [TryCast](memberInfo,PropertyInfo).PropertyType)
                            If Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, memberType, validationContext.Access) Then
                                item = New ValidationError(SR.GetString("Error_TargetTypeDataSourcePathMismatch", New Object() { validationContext.TargetType.FullName }), &H141) { _
                                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                }
                            End If
                        End If
                    End If
                End If
                If (item Is Nothing) Then
                    If TypeOf memberInfo Is PropertyInfo Then
                        Dim info3 As PropertyInfo = [TryCast](memberInfo,PropertyInfo)
                        If (Not info3.CanRead AndAlso ((validationContext.Access And AccessTypes.Read) <> 0)) Then
                            item = New ValidationError(SR.GetString("Error_PropertyNoGetter", New Object() { info3.Name, bind.Path }), &H142) { _
                                .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                            }
                        ElseIf (Not info3.CanWrite AndAlso ((validationContext.Access And AccessTypes.Write) <> 0)) Then
                            item = New ValidationError(SR.GetString("Error_PropertyNoSetter", New Object() { info3.Name, bind.Path }), &H143) { _
                                .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                            }
                        End If
                    ElseIf TypeOf memberInfo Is FieldInfo Then
                        Dim info4 As FieldInfo = [TryCast](memberInfo,FieldInfo)
                        If (((info4.Attributes And (FieldAttributes.Literal Or FieldAttributes.InitOnly)) <> FieldAttributes.PrivateScope) AndAlso ((validationContext.Access And AccessTypes.Write) <> 0)) Then
                            item = New ValidationError(SR.GetString("Error_ReadOnlyField", New Object() { info4.Name }), &H145) { _
                                .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                            }
                        End If
                    End If
                End If
            End If
            If (Not item Is Nothing) Then
                errors.Add(item)
            End If
            Return errors
        End Function

    End Class
End Namespace

